package com.rt.generalframework.core.util;


import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.lang3.StringEscapeUtils;

import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;

/**
 * Jaxb工具类 xml和java类相互转换
 * 
 * @author sunjianbo
 * @date 2017年4月17日
 */
public class JaxbXmlUtil {

	private static final String DEFAULT_ENCODING = "UTF-8";

	/**
	 * pojo转换成xml 默认编码UTF-8
	 * 
	 * @param obj
	 *            待转化的对象
	 * @return xml格式字符串
	 * @throws Exception
	 *             JAXBException
	 */
	public static String convertToXml(Object obj) {
		return convertToXml(obj, DEFAULT_ENCODING, false);
	}
	
	/**
	 * pojo转换成xml 默认编码UTF-8（不忽略字段为null的属性的生成）
	 * 
	 * @param obj
	 *            待转化的对象
	 * @return xml格式字符串
	 * @throws Exception
	 *             JAXBException
	 */
	public static String convertToXmlNotIgnoreBlank(Object obj) {
		return convertToXml(obj, DEFAULT_ENCODING, true);
	}

	/**
	 * pojo转换成xml
	 * 
	 * @param obj
	 *            待转化的对象
	 * @param encoding
	 *            编码
	 * @param notIgnoreBlank
	 * 			     不忽略null的属性
	 * @return xml格式字符串
	 * @throws Exception
	 *             JAXBException
	 */
	private static String convertToXml(Object obj, String encoding, boolean notIgnoreBlank) {
		String result = null;

		try {
			JAXBContext context = JAXBContext.newInstance(obj.getClass());
			Marshaller marshaller = context.createMarshaller();
			// 指定是否使用换行和缩排对已编组 XML 数据进行格式化的属性名称。
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
			if (notIgnoreBlank) {
				marshaller.setListener(new MarshallerListener());
			}

			StringWriter writer = new StringWriter();
			marshaller.marshal(obj, writer);
			result = writer.toString();
		} catch (Exception e) {
			throw new RuntimeException("解析失败", e);
		}

		return result;
	}

	/**
	 * xml转换成JavaBean
	 * 
	 * @param xml
	 *            xml格式字符串
	 * @param clazz
	 *            待转化的对象类
	 * @return 转化后的对象
	 * @throws Exception
	 *             JAXBException
	 */
	@SuppressWarnings("unchecked")
	public static <T> T convertToJavaBean(String xml, Class<T> clazz) {
		T obj = null;
		try {
			JAXBContext context = JAXBContext.newInstance(clazz);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			obj = (T) unmarshaller.unmarshal(new StringReader(xml));
		} catch (JAXBException e) {
			throw new RuntimeException("解析失败", e);
		}
		return obj;
	}
	
	/**
	 * 该监听器主要用来解决jaxb无法渲染字段为null的属性.
	 * <p>
	 * 在将Java类转换为xml片段时,默认jaxb会过滤属性值为null的属性,通过该监听器的{@link #beforeMarshal(Object)}
	 * 方法，在渲染前 通过将属性赋值为空字符串来达到在生成的xml片段中包含该属性的功能.
	 * </p>
	 * <p>
	 * <strong>注意:</strong>默认处理{@link String}类型的字段.
	 * </p>
	 */
	public static class MarshallerListener extends Marshaller.Listener {
		
		public static final String BLANK_CHAR = "";

		@Override
		public void beforeMarshal(Object source) {
			super.beforeMarshal(source);
			Field[] fields = source.getClass().getDeclaredFields();
			for (Field f : fields) {
				f.setAccessible(true);
				// 获取字段上注解<pre name="code" class="java">
				try {
					if (f.getType() == String.class && f.get(source) == null) {
						f.set(source, BLANK_CHAR);
					}
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	
	/**
	 * 删除xml请求内容的head（如带有&lt;?xml version="1.0"?&gt;）
	 * @param xmlStr xml字符串
	 * @return
	 */
	public static String delXmlHead(String xmlStr) {
		xmlStr = StringEscapeUtils.unescapeXml(xmlStr);
		if (xmlStr.indexOf("<?") >= 0) {
			int beginIndex = xmlStr.indexOf("<?");
			int endIndex = xmlStr.indexOf("?>", beginIndex);
			String xmlHead = xmlStr.substring(beginIndex, beginIndex + endIndex + 2);
			xmlStr = xmlStr.replace(xmlHead, "");
		}
		return xmlStr.trim();
	}
	
}